
local ffi = require 'ffi'
local jass = require 'jass.common'
local japi = require 'jass.japi'
local GetUnitAddress = japi.GetUnitAddress
local GetUnitAbilityLevel = jass.GetUnitAbilityLevel
local memorylib = require 'util.memory'
local handlelib = require 'util.handle'
local lflag = require 'util.flag'
local export = {}
local funcptrs = {}

ffi.cdef [[
    int GetModuleHandleA(const char* name) 
]]

local GameDLLAddress = ffi.C.GetModuleHandleA("game.dll")

function export.GameDLL()
    return GameDLLAddress
end

-- 单位地址 + 0x1E8 + 偏移0x218         攻击1目标允许
-- 单位地址 + 0x1E8 + 偏移0x21C         攻击2目标允许

-- 单位地址 + 偏移0x1E8   攻击 Aatk
-- 单位地址 + 偏移0x1Ec   移动 Amov
-- 单位地址 + 偏移0x1F0   英雄 AHer 只有英雄才有
-- 单位地址 + 偏移0x1F4   建造 AHbu 没有建造能力的单位获取不到
-- 单位地址 + 偏移0x1F8   背包技能


---根据单位技能ID找到技能对象的地址
function export.GetUnitAbilityAddress(handle, aid)
    if not handlelib.is_unit(handle) then 
        return 0
    end
    if not aid or aid == 0 or GetUnitAbilityLevel(handle, aid) < 1 then 
        return 0
    end  
    if not funcptrs['GetUnitAbility'] then 
        local function_type = ffi.typeof("uint32_t(__fastcall*)(uint32_t,uint32_t,uint32_t,uint32_t,uint32_t,uint32_t,uint32_t)")
        local intptr = ffi.new("uint32_t[1]")
        intptr[0] = GameDLLAddress + 0x46F440          -- 127 的
        funcptrs['GetUnitAbility'] = ffi.cast(function_type, intptr)
    end 
    local funcptr = funcptrs['GetUnitAbility']
    return funcptr(GetUnitAddress(handle), 0, aid, 0, 1, 1, 1)
end 

---设置技能沉默
function export.SilentUnitAbility(handle, aid, flag)
    local pabi = export.GetUnitAbilityAddress(handle, aid)
    if pabi >= 0 then 
        memorylib.write(pabi + 0x3C, flag and 1 or -1, 'int')
    end
end

---设置单位的可移动性 效果跟被诱捕的状态一样 需要用 EXPauseUnit 配合一下让单位停止移动
---@param handle handle 单位句柄
---@param flag boolean 是否可以移动
function export.SetUnitMovement(handle, flag)
    if handlelib.is_unit(handle) then 
        local code = memorylib.read(GetUnitAddress(handle) + 0x1EC, 'int')
        if code then 
            japi.EXPauseUnit(handle, true)
            memorylib.write(code + 0x7C, flag and 0 or 1, 'int')
            japi.EXPauseUnit(handle, false)
        end 
    end
end 

---设置血条高度
function export.SetUnitPressUIHeight(handle, height)
    if not height or type(height) ~= "number" then return end;
    if handlelib.is_unit(handle) then 
        memorylib.write(GetUnitAddress(handle) + 0x2cc, height * 1, 'float')
    end
end 

-- 攻击 -> 0x20 的flag测试记录
-- local uint32_t = 20080195

-- local code = ""

-- if uint32_t <= 0 then return end
-- for i = 1, 11 do
--     if uint32_t % 2 == 1 then
--         code = code .. "1"
--     else 
--         code = code .. "0"
--     end
--     uint32_t = math.floor(uint32_t / 2)
--     if uint32_t <= 0 then break end
-- end

-- print( code )

-- 01011011101 右击地面
-- 11010100101 攻击地面
-- 11111011101 攻击单位
-- 11001000010 攻击中
-- 01010101100 停止 
-- 01010100101 保持原位
-- 11001001101 保持原位攻击中 
-- 11001000010 停止命令攻击中
-- 11000010011 攻击命令攻击中



---获取单位行动状态
-- stop = 0
-- attack = 1
-- spell = 3
-- dead = 4
-- harvest = 5
-- move = 7
-- function export.GetUnitActorState(handle)
--     if not handlelib.is_unit(handle) then 
--         return 0
--     end
--     if GetUnitAbilityLevel(handle, FourCC 'Amov') == 0 then 
--         return 0
--     end 
--     return memorylib.read(japi.GetUnitAddress(handle) + 0x194, 'int')
-- end

-- -- 获取单位当前的模型动画ID(异步的)
-- function export.GetUnitCurrentAnim(handle)
--     if not handlelib.is_unit(handle) then 
--         return 0
--     end
--     local phandle = japi.GetUnitAddress(handle)
--     local psprite = memorylib.read(phandle + 0x28, 'int') 
--     if psprite and psprite > 0 then 
--         return memorylib.read(psprite + 0x1D0, 'int') 
--     end 
--     return 0
-- end

function export.FrameGetFontHeight(frame)
    if frame and frame ~= 0 and japi.FrameGetType(frame) == 'TEXT' then 
        return memorylib.read(frame + 0x34, 'float')
    end 
    return 0
end

function export.FrameSetFontHeight(frame, height)
    if frame and frame ~= 0 and japi.FrameGetType(frame) == 'TEXT' then 
        memorylib.write(frame + 0x34, height * 1, 'float')
        japi.FrameSetScale(frame, 1.0)
    end 
end

function export.FrameGetFontAlpha(frame)
    if frame and frame ~= 0 and japi.FrameGetType(frame) == 'TEXT' then 
        return memorylib.read(frame + 0xAC, 'uint32_t')
    end 
    return 0
end

function export.FrameSetFontAlpha(frame, alpha)
    if frame and frame ~= 0 and japi.FrameGetType(frame) == 'TEXT' then 
        memorylib.write(frame + 0xAC, alpha * 1, 'uint32_t')
        japi.FrameSetScale(frame, 1.0)
    end 
end

function export.SetEditBoxDisableImme(editframe, flag)
    if editframe and editframe ~= 0 and japi.FrameGetType(editframe) == 'EDITBOX' then 
        memorylib.write(editframe + 0X204, flag and 0 or 1, 'uint32_t')
    end 
end

-- function export.EXSetUnitTimedLife(handle, time)
--     if not handlelib.is_unit(handle) or type(time) ~= 'number' then 
--         return 0
--     end
--     local pabi = export.GetUnitAbilityAddress(handle, FourCC 'BTLF')
--     if not pabi or pabi == 0 then 
--         return 
--     end 
--     local pdata = memorylib.read(pabi + 0x90, 'uint32_t')
--     if not pdata or pdata == 0 then 
--         return 
--     end 
--     memorylib.write(pdata + 0x4, jass.Pow(time, 1.0), 'float')
-- end


-- 内置 开全图
-- local state = 2 ^ 4
-- local value = memorylib.read(GetUnitAddress(tester.handle) + 0x20, 'int')
-- if not lflag.hasflag(value , state) then 
--     memorylib.write(GetUnitAddress(tester.handle) + 0x20, value + state, 'int')
-- else
--     memorylib.write(GetUnitAddress(tester.handle) + 0x20, value - state, 'int')
-- end 


-- -- 攻击1 目标允许
-- punit + 0x1E8 + 0x218
-- -- 攻击2 目标允许
-- punit + 0x1E8 + 0x21C

function export.LocationMobility(x, y, flags)
    if not x or not y or not flags then return false end;
    if not funcptrs['LocationMobility'] then 
        local function_type = ffi.typeof[[
            uint32_t (__fastcall*) (float*, float*, uint32_t, uint32_t)
        ]]
        local intptr = ffi.new("uint32_t[1]")
        intptr[0] = GameDLLAddress + 0x30E80          -- 127 的
        funcptrs['LocationMobility'] = ffi.cast(function_type, intptr)
    end 
    local funcptr = funcptrs['LocationMobility']
    local x_ptr = ffi.new("float[1]");  x_ptr[0] = x
    local y_ptr = ffi.new("float[1]");  y_ptr[0] = y
    return (funcptr( x_ptr, y_ptr, flags, 0) == 0)
end 

function export.SetUnitAsTargetType(handle, flag)
    if not flag or type(flag) ~= "number" then 
        return 
    end 
    if not handlelib.is_unit(handle) then 
        return 
    end
    local punit = GetUnitAddress(handle)
    if not punit or punit == 0 then 
        return 
    end 
	local ptr = ffi['cast']('int*', punit)
	ptr[0x24c / 4] = (math.ceil(flag)) 
end 

function export.GetUnitAsTargetType(handle)
    if not handlelib.is_unit(handle) then 
        return 0
    end
    local punit = GetUnitAddress(handle)
    if not punit or punit == 0 then 
        return 0 
    end 
    local ptr = ffi['cast']('int*', punit)
	return (ptr[0x24c / 4]) * 1
end 

return export